 
import json
import math 
import socket
import threading
import time


class TModbusSys:
    def __init__(self) -> None:
        self.version = "modbus-sys-2024"
        self.devs ={}
    def loadObjs(self , devs , tasks):
        try:
            for d in devs:
                dev = TModbusDev()
                dev.devCode = d['devCode']
                dev.devHost = d['devHost']
                dev.devPort = d['devPort']
                self.devs[dev.devCode] = dev  
            for t in tasks:
                dev = self.devs[t['devCode']]
                task = TModbusTask(dev)
                task.taskId = t['taskId']
                task.unitId = t['unitId']
                task.regAddr = t['regAddr']
                task.regNum = t['regNum']
                task.funCode = t['funCode']
                task.updateCmdBytes()
                dev.taskList.append(task)
                regAddr = task.regAddr
                regMaxAddr = regAddr + task.regNum
                while regAddr < regMaxAddr:
                    reg = TModbusRegister()
                    reg.devCode = dev.devCode
                    reg.regAddr = regAddr
                    regKey = dev.devCode +'_'+  str(task.unitId)+"_"+ str(task.funCode)+"_"+ str(regAddr)
                    reg.regKey = regKey
                    reg.unitId = task.unitId 
                    reg.funCode = task.funCode
                    dev.registers[regKey] =  reg
                    regAddr = regAddr + 1
            for v in vars:
                dev = self.devs[v['devCode']]
                var = TModbusVar(dev)
                var.varId = v['varId']
                var.varType = v['varType']
                regKey = v['regKey']
                var.register = dev.registers[regKey]
                dev.vars[var.varId] = var
                var.register.vars.append(var)
        except Exception as er :
            print(er)
    def start(self):
        try:
            for dev in self.devs.values():
                dev.startAsync();
                #dev.start();
        except Exception as er :
            print(er)
    
    def routerHandlerStart(self, result , request , router):
        try:
            self.start();
            result["status"] = 1;
            result["datas"] ={};
        except Exception as er:
            print(er)
    def routerHandlerGetRegisters(self, result , request , router):
        try:            
            ps = router.getRequestParams(request)
            if 'devCode' in ps.keys():
                devCode = ps['devCode']
                res ={}
                for dev in self.devs.values():
                    flag = dev.devCode == devCode or devCode=='all'
                    if flag :
                        res[dev.devCode] = dev.getRegisterDatas()
                result["status"] = 1
                result["datas"] = res
            else:
                result["status"] = -1
                result["error"] = "devCode is error" 
        except Exception as er:
            print(er)


class TModbusDev:
    def __init__(self) -> None:
        self.devCode = ""
        self.devHost = ""
        self.devPort = 502
        self.registers={};
        self.taskList=[]


        self.registTasks = {}
        self.vars ={}

        self.taskIndex = -1
        self.txNo = 0
        self.isActived = False
        self.skt = None
        self.readThread = None

    def start(self):
        try:
            if self.isActived :
                return
            self.isActived = True
            address = (self.devHost , self.devPort)
            self.skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.skt.settimeout(5)
            self.skt.connect(address) 
            self.taskIndex = -1
            print('-----------  start modbus tcp device ['+self.devCode+'] -------------')
            while 1>0 :
                if self.skt!=None and (not self.skt._closed) :
                    self.taskIndex = self.taskIndex + 1
                    if self.taskIndex>=len(self.taskList):
                        self.taskIndex = 0
                    task = self.taskList[self.taskIndex]
                    task.readDatas()
                else:
                    break
            self.isActived = False
        except Exception as er :
            print(er)
        finally:
            self.isActived = False
    def startAsync(self):
        try:
            self.readThread = threading.Thread( target= self.start , daemon=True)
            self.readThread.start()
        except Exception as er :
            print(er)
    def getRegisterDatas(self):
        res = {}
        try:
            for r in self.registers.values():
                res[r.regKey] = r.getDict()
        except Exception as er:
            print(er)
        return res
class TModbusTask:
    def __init__(self , dev) -> None:
        self.dev = dev
        self.taskId = 0
        self.unitId = 0
        self.funCode = 0x01
        self.regAddr = 0 
        self.regNum = 0  
        self.cmdBytes = bytearray([
            0x00,0x00,
            0x00,0x00,
            0x00,0x06,
            0x00,0x00,
            0x00,0x00,
            0x00,0x00
        ])
    def readDatas(self):
        try:
            self.dev.txNo = self.dev.txNo + 1
            tranBytes = int.to_bytes( self.dev.txNo , 2 , byteorder='big' )  
            self.cmdBytes[0] = tranBytes[0]
            self.cmdBytes[1] = tranBytes[1]
            self.dev.skt.send(self.cmdBytes)
            txtNoKey = str(self.cmdBytes[0])+'-'+str(self.cmdBytes[1])
            self.dev.registTasks[txtNoKey] = self
            time.sleep(0.01)
            recBytes = self.dev.skt.recv(1024)
            checkFlag = self.checkRecBytes(recBytes)
            if checkFlag :
                self.setRegisters(recBytes)
        except Exception as er:
            print(er)  
    def updateCmdBytes(self):
        res = None
        try:
            tranBytes = int.to_bytes( self.dev.txNo , 2 , byteorder='big' ) 
            regAddrBytes = int.to_bytes(self.regAddr , 2 , byteorder='big' ) 
            regNumBytes = int.to_bytes( self.regNum , 2 , byteorder='big' )  
            self.cmdBytes[0] = tranBytes[0]
            self.cmdBytes[1] = tranBytes[1]
            self.cmdBytes[6] = self.unitId
            self.cmdBytes[7] = self.funCode
            self.cmdBytes[8] = regAddrBytes[0]
            self.cmdBytes[9] = regAddrBytes[1]
            self.cmdBytes[10] = regNumBytes[0]
            self.cmdBytes[11] = regNumBytes[1]
        except Exception as er:
            print(er) 
        return res    
    def checkRecBytes(self, bytes):
        res = False
        try:
            res = len(bytes)>9
        except Exception as er:
            print(er)
        return res  
    def setRegisters(self, recBytes): 
        try: 
            txNoKey = str(recBytes[0])+'-'+str(recBytes[1])
            task = self.dev.registTasks[txNoKey] 
            regAddr = task.regAddr
            regNum = task.regNum
            pos =0
            offset = 9
            while pos < regNum:
                try:
                    regKey = task.dev.devCode +"_" + str(task.unitId)+'_'+ str(task.funCode)+'_'+ str(regAddr + pos)
                    register = task.dev.registers[regKey] 
                    if task.funCode == 1 or task.funCode == 2 :
                        bitInd = pos % 8 
                        byteInd = math.floor( pos*0.125 )
                        byteData = recBytes[offset + byteInd]
                        val = (byteData>>bitInd) % 2 
                        register.setBit(val)
                    elif task.funCode == 3 or task.funCode == 4 :                        
                        h = recBytes[offset + 2*pos]
                        l = recBytes[offset + 2*pos + 1]
                        register.setHeightLow(h , l)
                    else:
                        print('----- decode faild--------')
                    pos = pos + 1
                except Exception as eer:
                    print(eer)
        except Exception as er:
            print(er)

class TModbusRegister:
    def __init__(self) -> None:
        self.regKey = ""
        self.regAddr=0
        self.unitId =0 
        self.funCode=0
        self.devCode = ""
        self.bit = 0 
        self.h = 0
        self.l = 0
        self.t = 0 


        self.vars =[]
    def getDict(self):
        return {
            "regKey": self.regKey ,
            "unitId": self.unitId ,
            "funCode": self.funCode ,
            "devCode": self.devCode ,
            "bit": self.bit ,
            "h": self.h ,
            "l": self.l ,
            "t": self.t
        }
    def setBit(self, bitVal):
        try:
            self.bit = bitVal 
            self.t = time.time()
        except Exception as er:
            print(er)
    def setHeightLow(self, h , l ):
        try:
            self.h = h 
            self.l = l  
            self.t = time.time()
        except Exception as er:
            print(er)
class TModbusVar:
    def __init__(self, dev) -> None:
        self.varId = ""
        self.varType = 'v01'
        self.register = None 
        self.dev = dev
from ..db import mssql

dbKey = 'modbusTcp'
sql = 'select * from v_sys'
sysObjs = mssql.mssql.connections[dbKey].query(sql, None)
devJson =sysObjs[0]['devs']
taskJson =sysObjs[0]['tasks']
varJson =sysObjs[0]['vars']
devs = json.loads(devJson)
tasks = json.loads(taskJson)
vars = json.loads(varJson)
print(sysObjs)

sys = TModbusSys() 
sys.loadObjs(devs,tasks)
print( 'start modbus tcp .')
#sys.start()